#include "packet_reorder.hpp"
#include "mod/rtp.h"

#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"

TEST_CASE("0,1,2", "[reorder]") {
    PacketReorder<RTPPacket> pr(3);
    std::vector<RTPPacket*> overflow;

    RTPPacket p0(MediaFrame::Type::Video, 1);
    RTPPacket p1(MediaFrame::Type::Video, 1);
    RTPPacket p2(MediaFrame::Type::Video, 1);
    RTPPacket p3(MediaFrame::Type::Video, 1);
    RTPPacket p4(MediaFrame::Type::Video, 1);
    RTPPacket p7(MediaFrame::Type::Video, 1);

    p0.SetSeqNum(0);
    p1.SetSeqNum(1);
    p2.SetSeqNum(2);
    p3.SetSeqNum(3);
    p4.SetSeqNum(4);
    p7.SetSeqNum(7);

    pr.add(overflow, &p0);
    REQUIRE(overflow.size() == 0);
    pr.add(overflow, &p1);
    REQUIRE(overflow.size() == 0);
    pr.add(overflow, &p2);
    REQUIRE(overflow.size() == 0);


    SECTION("+3 -> 0 + 1,2,3") {
        pr.add(overflow, &p3);

        REQUIRE(overflow.size() == 1);
        REQUIRE(overflow[0] == &p0);

        std::vector<RTPPacket*> remaining;
        pr.reset(remaining);

        REQUIRE(remaining.size() == 3);
        REQUIRE(remaining[0] == &p1);
        REQUIRE(remaining[1] == &p2);
        REQUIRE(remaining[2] == &p3);

    }

    SECTION("+4 -> 0,1 + 2,,4") {
        pr.add(overflow, &p4);

        REQUIRE(overflow.size() == 2);
        REQUIRE(overflow[0] == &p0);
        REQUIRE(overflow[1] == &p1);

        std::vector<RTPPacket*> remaining;
        pr.reset(remaining);

        REQUIRE(remaining.size() == 2);
        REQUIRE(remaining[0] == &p2);
        REQUIRE(remaining[1] == &p4);
    }

}

TEST_CASE("2,4", "[reorder]") {
    PacketReorder<RTPPacket> pr(3);
    std::vector<RTPPacket*> overflow;

    RTPPacket p0(MediaFrame::Type::Video, 1);
    RTPPacket p1(MediaFrame::Type::Video, 1);
    RTPPacket p2(MediaFrame::Type::Video, 1);
    RTPPacket p3(MediaFrame::Type::Video, 1);
    RTPPacket p4(MediaFrame::Type::Video, 1);
    RTPPacket p7(MediaFrame::Type::Video, 1);

    p0.SetSeqNum(0);
    p1.SetSeqNum(1);
    p2.SetSeqNum(2);
    p3.SetSeqNum(3);
    p4.SetSeqNum(4);
    p7.SetSeqNum(7);

    pr.add(overflow, &p2);
    REQUIRE(overflow.size() == 0);
    pr.add(overflow, &p4);
    REQUIRE(overflow.size() == 0);

    SECTION("+1 ->  + 2,,4") {
        bool added = pr.add(overflow, &p1);
        REQUIRE(added == false);
        REQUIRE(overflow.size() == 0);

        std::vector<RTPPacket*> remaining;
        pr.reset(remaining);

        REQUIRE(remaining.size() == 2);
        REQUIRE(remaining[0] == &p2);
        REQUIRE(remaining[1] == &p4);
    }

    SECTION("+2 ->  + 2,,4") {
        bool added = pr.add(overflow, &p2);
        REQUIRE(added == false);
        REQUIRE(overflow.size() == 0);

        std::vector<RTPPacket*> remaining;
        pr.reset(remaining);

        REQUIRE(remaining.size() == 2);
        REQUIRE(remaining[0] == &p2);
        REQUIRE(remaining[1] == &p4);
    }

    SECTION("+3 ->  + 2,3,4") {
        pr.add(overflow, &p3);

        REQUIRE(overflow.size() == 0);

        std::vector<RTPPacket*> remaining;
        pr.reset(remaining);

        REQUIRE(remaining.size() == 3);
        REQUIRE(remaining[0] == &p2);
        REQUIRE(remaining[1] == &p3);
        REQUIRE(remaining[2] == &p4);
    }

    SECTION("+4 ->  + 2,,4") {
        bool added = pr.add(overflow, &p4);
        REQUIRE(added == false);
        REQUIRE(overflow.size() == 0);

        std::vector<RTPPacket*> remaining;
        pr.reset(remaining);

        REQUIRE(remaining.size() == 2);
        REQUIRE(remaining[0] == &p2);
        REQUIRE(remaining[1] == &p4);
    }

}

TEST_CASE("3,4", "[reorder]") {
    PacketReorder<RTPPacket> pr(3);
    std::vector<RTPPacket*> overflow;

    RTPPacket p0(MediaFrame::Type::Video, 1);
    RTPPacket p1(MediaFrame::Type::Video, 1);
    RTPPacket p2(MediaFrame::Type::Video, 1);
    RTPPacket p3(MediaFrame::Type::Video, 1);
    RTPPacket p4(MediaFrame::Type::Video, 1);
    RTPPacket p7(MediaFrame::Type::Video, 1);

    p0.SetSeqNum(0);
    p1.SetSeqNum(1);
    p2.SetSeqNum(2);
    p3.SetSeqNum(3);
    p4.SetSeqNum(4);
    p7.SetSeqNum(7);

    pr.add(overflow, &p3);
    REQUIRE(overflow.size() == 0);
    pr.add(overflow, &p4);
    REQUIRE(overflow.size() == 0);



    SECTION("+2 ->  + 2,3,4") {
        pr.add(overflow, &p2);

        REQUIRE(overflow.size() == 0);

        std::vector<RTPPacket*> remaining;
        pr.reset(remaining);

        REQUIRE(remaining.size() == 3);
        REQUIRE(remaining[0] == &p2);
        REQUIRE(remaining[1] == &p3);
        REQUIRE(remaining[2] == &p4);

    }

}


TEST_CASE("2,3,4", "[reorder]") {
    PacketReorder<RTPPacket> pr(3);
    std::vector<RTPPacket*> overflow;

    RTPPacket p0(MediaFrame::Type::Video, 1);
    RTPPacket p1(MediaFrame::Type::Video, 1);
    RTPPacket p2(MediaFrame::Type::Video, 1);
    RTPPacket p3(MediaFrame::Type::Video, 1);
    RTPPacket p4(MediaFrame::Type::Video, 1);
    RTPPacket p7(MediaFrame::Type::Video, 1);

    p0.SetSeqNum(0);
    p1.SetSeqNum(1);
    p2.SetSeqNum(2);
    p3.SetSeqNum(3);
    p4.SetSeqNum(4);
    p7.SetSeqNum(7);

    pr.add(overflow, &p2);
    REQUIRE(overflow.size() == 0);
    pr.add(overflow, &p3);
    REQUIRE(overflow.size() == 0);
    pr.add(overflow, &p4);
    REQUIRE(overflow.size() == 0);



    SECTION("+7 -> 2,3,4 + ,,7") {
        pr.add(overflow, &p7);

        REQUIRE(overflow.size() == 3);
        REQUIRE(overflow[0] == &p2);
        REQUIRE(overflow[1] == &p3);
        REQUIRE(overflow[2] == &p4);

        std::vector<RTPPacket*> remaining;
        pr.reset(remaining);

        REQUIRE(remaining.size() == 1);
        REQUIRE(remaining[0] == &p7);

    }

}

TEST_CASE("65533,65534,65535", "[wrapping]") {
    PacketReorder<RTPPacket> pr(3);
    std::vector<RTPPacket*> overflow;

    RTPPacket p65533(MediaFrame::Type::Video, 1);
    RTPPacket p65534(MediaFrame::Type::Video, 1);
    RTPPacket p65535(MediaFrame::Type::Video, 1);
    RTPPacket p0(MediaFrame::Type::Video, 1);
    RTPPacket p3000(MediaFrame::Type::Video, 1);

    p65533.SetSeqNum(65533);
    p65534.SetSeqNum(65534);
    p65535.SetSeqNum(65535);
    p0.SetSeqNum(0);
    p3000.SetSeqNum(3000);

    pr.add(overflow, &p65533);
    REQUIRE(overflow.size() == 0);
    pr.add(overflow, &p65534);
    REQUIRE(overflow.size() == 0);
    pr.add(overflow, &p65535);
    REQUIRE(overflow.size() == 0);

    SECTION("+0 -> o 65533 k 65534,65535,0") {
        pr.add(overflow, &p0);
        REQUIRE(overflow.size() == 1);
        REQUIRE(overflow[0]->GetSeqNum() == 65533);
    }
    SECTION("+3000 -> o nothing k 65533,65534,65535") {
        bool added = pr.add(overflow, &p3000);
        REQUIRE(added == false);
        REQUIRE(overflow.size() == 0);
        std::vector<RTPPacket*> remains;
        pr.dump_packets();
        pr.reset(remains);
        REQUIRE(remains.size() == 3);
    }

}

TEST_CASE("0-65535->0-65535", "[reset]") {
    PacketReorder<RTPPacket> pr(3);
    std::vector<RTPPacket*> overflow;

    for (int i = 2000; i <= 65535; ++i) {
        RTPPacket* p = new RTPPacket(MediaFrame::Type::Video, 1);
        p->SetSeqNum(i);
        pr.add(overflow, p);
    }
    for (int i = 0; i <= 1000; ++i) {
        RTPPacket* p = new RTPPacket(MediaFrame::Type::Video, 1);
        p->SetSeqNum(i);
        pr.add(overflow, p);
    }

    std::vector<RTPPacket*> remains;
    pr.reset(remains);
    REQUIRE(remains.size() == 3);
}